﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.UI;

namespace Quintsys.Core.Extensions
{
    public static class StringExtensions
    {
        /// <summary>
        /// Truncates the specified string.
        /// </summary>
        /// <param name="input">The string.</param>
        /// <param name="maxLength">Maximum desired length.</param>
        /// <returns></returns>
        public static string Truncate(this string input, int maxLength)
        {
            if (String.IsNullOrEmpty(input) || maxLength <= 0)
                return String.Empty;
            return input.Length > maxLength
                       ? input.Substring(0, maxLength) + "..."
                       : input;
        }

        /// <summary>
        /// Removes non digits from the string.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static string ToDigitsOnly(this string input)
        {
            return String.IsNullOrEmpty(input)
                       ? input
                       : new string(input.Where(char.IsDigit).ToArray());
        }

        /// <summary>
        /// Determines whether the specified input is valid credit card number.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static bool IsValidCreditCardNumber(this string input)
        {
            var deltas = new[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 0 };
            var checksum = 0;
            var chars = input.ToCharArray();
            for (var i = chars.Length - 1; i > -1; i--)
            {
                var j = chars[i] - 48;
                checksum += j;
                if (((i - chars.Length) % 2) == 0)
                    checksum += deltas[j];
            }

            return ((checksum % 10) == 0);
        }

        /// <summary>
        /// Remaps the international chars to ASCII.
        /// </summary>
        /// <param name="c">The c.</param>
        /// <returns></returns>
        public static string RemapInternationalCharToAscii(char c)
        {
            var s = c.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
            if ("àåáâäãåą".Contains(s))
            {
                return "a";
            }
            if ("èéêëę".Contains(s))
            {
                return "e";
            }
            if ("ìíîïı".Contains(s))
            {
                return "i";
            }
            if ("òóôõöøőð".Contains(s))
            {
                return "o";
            }
            if ("ùúûüŭů".Contains(s))
            {
                return "u";
            }
            if ("çćčĉ".Contains(s))
            {
                return "c";
            }
            if ("żźž".Contains(s))
            {
                return "z";
            }
            if ("śşšŝ".Contains(s))
            {
                return "s";
            }
            if ("ñń".Contains(s))
            {
                return "n";
            }
            if ("ýÿ".Contains(s))
            {
                return "y";
            }
            if ("ğĝ".Contains(s))
            {
                return "g";
            }
            if (c == 'ř')
            {
                return "r";
            }
            if (c == 'ł')
            {
                return "l";
            }
            if (c == 'đ')
            {
                return "d";
            }
            if (c == 'ß')
            {
                return "ss";
            }
            if (c == 'Þ')
            {
                return "th";
            }
            if (c == 'ĥ')
            {
                return "h";
            }
            if (c == 'ĵ')
            {
                return "j";
            }
            return "";
        }

        /// <summary>
        /// Produces optional, URL-friendly version of a title, "like-this-one". 
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static string ToUrlFriendly(this string input)
        {
            if (input == null) return "";

            const int maxlen = 80;
            var len = input.Length;
            var prevdash = false;
            var sb = new StringBuilder(len);

            for (var i = 0; i < len; i++)
            {
                var c = input[i];
                if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
                {
                    sb.Append(c);
                    prevdash = false;
                }
                else if (c >= 'A' && c <= 'Z')
                {
                    // tricky way to convert to lowercase
                    sb.Append((char)(c | 32));
                    prevdash = false;
                }
                else if (c == ' ' || c == ',' || c == '.' || c == '/' ||
                         c == '\\' || c == '-' || c == '_' || c == '=')
                {
                    if (!prevdash && sb.Length > 0)
                    {
                        sb.Append('-');
                        prevdash = true;
                    }
                }
                else if (c >= 128)
                {
                    var prevlen = sb.Length;
                    sb.Append(RemapInternationalCharToAscii(c));
                    if (prevlen != sb.Length) prevdash = false;
                }
                if (i == maxlen) break;
            }

            return prevdash
                       ? sb.ToString().Substring(0, sb.Length - 1)
                       : sb.ToString();
        }

        /// <summary>
        /// Determines whether [is valid redirect URL] [the specified return URL].
        /// </summary>
        /// <param name="returnUrl">The return URL.</param>
        /// <returns>
        ///   <c>true</c> if [is valid redirect URL] [the specified return URL]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsValidRedirectUrl(this string returnUrl)
        {
            if (String.IsNullOrEmpty(returnUrl))
                return false;

            return returnUrl.StartsWith("/") &&
                   !returnUrl.StartsWith("//") &&
                   !returnUrl.StartsWith("/\\");
        }

        /// <summary>
        /// FormatWith is an extension method that wraps String.Format. 
        /// Updated w/ an overload that takes a single argument and allows 
        /// the use of property names in the format string.
        /// </summary>
        /// <param name="format"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string FormatWith(this string format, object source)
        {
            return FormatWith(format, null, source);
        }

        /// <summary>
        /// FormatWith is an extension method that wraps String.Format. 
        /// Updated w/ an overload that takes a single argument and allows 
        /// the use of property names in the format string.
        /// </summary>
        /// <param name="format"></param>
        /// <param name="provider"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string FormatWith(this string format, IFormatProvider provider, object source)
        {
            if (format == null)
                throw new ArgumentNullException("format");

            var r = new Regex(@"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
                              RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);

            var values = new List<object>();
            var rewrittenFormat = r.Replace(format, delegate(Match m)
            {
                var startGroup = m.Groups["start"];
                var propertyGroup = m.Groups["property"];
                var formatGroup = m.Groups["format"];
                var endGroup = m.Groups["end"];

                values.Add((propertyGroup.Value == "0")
                               ? source
                               : DataBinder.Eval(source,
                                                 propertyGroup.Value));

                return new string('{', startGroup.Captures.Count) +
                       (values.Count - 1) + formatGroup.Value
                       + new string('}', endGroup.Captures.Count);
            });

            return string.Format(provider, rewrittenFormat, values.ToArray());
        }

        /// <summary>
        /// Converts the specified string to title case.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static string ToTitleCase(this string input)
        {
            if (string.IsNullOrEmpty(input))
                return input;

            var cultureInfo = Thread.CurrentThread.CurrentCulture;
            var textInfo = cultureInfo.TextInfo;

            return textInfo.ToTitleCase(input);
        }

        /// <summary>
        /// Converts the specified string to formatted us phone.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static string ToFormattedUsPhone(this string input)
        {
            if (string.IsNullOrEmpty(input) || input.Length < 10)
                return null;

            return string.Format("({0}) {1}-{2}", input.Substring(0, 3), input.Substring(3, 3), input.Substring(6));
        }
    }
}